#!/usr/bin/env python
# encoding: utf-8


"""
@file: triangle_objects.py
@time: 2017/4/6 下午6:26
"""
from functools import reduce
from mathsolver.functions.base import *
from sympy import expand_trig, sin, cos, pi, simplify, sqrt, solveset
from mathsolver.functions.sanjiao import sanjiao_utils as su
from itertools import chain
from mathsolver.functions.budengshi import common_opers as co
from collections import deque
from itertools import permutations, combinations
from mathsolver.functions.sanjiao import sanjiaoxing_utils as sjx_utils


# 在集合关系的时候运算的时候 规定 所有的点都用Point表示, 所有的边都用Edge表示。
# 构造点的时候用单个字符串构造, 构造边的时候用含2个字母的字符串

# 基础几何
class Geometry(object):
    pass


# 点
class Point(Geometry):
    def __init__(self, point_name):
        if isinstance(point_name, Point):
            point_name = str(point_name)
        self._point_name = str(point_name)

    @property
    def point_name(self):
        """
        点的名字
        :return: 
        """
        return self._point_name

    def __str__(self):
        return self._point_name

    def __eq__(self, other):
        return self._point_name == str(other)

    def __hash__(self):
        return str.__hash__(self._point_name)


# 边
class Edge(Geometry):
    @staticmethod
    def _stand_edge_name(e_n):
        """
        标准化边的名字
        :param e_n: 
        :return: 
        """
        return ''.join(sorted(list(str(e_n))))

    def __init__(self, edge_name):
        """
        边的名字 例如 AB, BC, AC
        :param edge_name: 
        """
        self._edge_name = Edge._stand_edge_name(edge_name)

    @property
    def edge_name(self):
        return ''.join(sorted(list(self._edge_name)))

    def edge_end_points(self):
        """
        边的两个端点
        :return: 
        """
        return map(Point, list(self.edge_name))

    def __str__(self):
        return self.edge_name

    def __eq__(self, other):  # 判断两条边相等
        if isinstance(other, str):
            other = Edge(other)
        return str(other) == str(self)


# 角
class Angle(Geometry):
    @staticmethod
    def _stand_angle_name(ag_name):
        """
        标准化角的名字
        :param ag_name: 
        :return: 
        """
        ag_name_str = str(ag_name)
        a_p = ag_name_str[1]
        p1, p2 = sorted([ag_name_str[0], ag_name_str[2]])
        return p1 + a_p + p2

    def __init__(self, angle_name, adj_edge1=None, adj_edge2=None):
        if isinstance(angle_name, Angle):
            angle_name = str(angle_name)
        self._angle_name = Angle._stand_angle_name(angle_name)
        if not adj_edge1 and not adj_edge2 and len(str(angle_name)) == 3:
            points = list(str(angle_name))
            self._adj_edge1 = Edge(''.join(sorted(points[:2])))
            self._adj_edge2 = Edge(''.join(sorted(points[1:])))
        elif adj_edge1 and adj_edge2:
            self._adj_edge1 = Edge(adj_edge1)
            self._adj_edge2 = Edge(adj_edge2)

    @property
    def angle_name(self):
        """
        角的名字
        :return: 
        """
        return str(self._angle_name)

    @property
    def angle_adj_edges(self):
        """
        角相邻的两条边
        :return: 
        """
        return self._adj_edge1, self._adj_edge2

    @property
    def angle_point(self):
        """
        角点 比如 角ABC=>B; 角ADC=>D
        :return: 
        """
        adj_edge1 = self.angle_adj_edges[0]
        adj_edge2 = self.angle_adj_edges[1]
        adj_edge1_points = map(str, adj_edge1.edge_end_points())
        adj_edge2_points = map(str, adj_edge2.edge_end_points())
        point = list(set(adj_edge1_points).intersection(set(adj_edge2_points)))[0]
        # point = list(FiniteSet(*adj_edge1_points).intersect(FiniteSet(*adj_edge2_points)))[0]
        return Point(point)

    @property
    def edge_points(self):
        """
        角的两个边的点
        :return: 
        """
        p1 = self.angle_name[0]
        p2 = self.angle_name[2]
        return Point(p1), Point(p2)

    def __eq__(self, other):  # 角点要相同，并且角的其他两个点也要相同
        if isinstance(other, str) and len(other) == 3:
            other = Angle(other)
        if not self.angle_point != other.angle_point:
            return False
        # ap_str = str(self.angle_point)
        # other_ps1 = set(list(str(self.angle_name))) - {ap_str}
        # other_ps2 = set(list(str(other))) - {ap_str}
        # return other_ps1 == other_ps2
        return set(list(self.angle_name)) == set(list(other.angle_name))

    def __str__(self):
        ap = self.angle_point
        others = sorted(list(set(list(self.angle_name)) - {str(ap)}))
        return others[0] + str(ap) + others[1]


# 位置关系
class LocationRelation(object):
    pass


# 点在边上关系
class PointOnTheEdge(LocationRelation):
    def __init__(self, point_name, edge_name):
        self.point_name = str(point_name)
        self.edge_name = str(edge_name)

    @property
    def point(self):
        """
        点
        :return: 
        """
        return Point(self.point_name)

    @property
    def edge(self):
        """
        边
        """
        return Edge(self.edge_name)

    def __eq__(self, other):
        if not isinstance(other, PointOnTheEdge):
            return False
        p = other.point
        if self.point.point_name != p.point_name:  # 首先要保证角点相同
            raise False
        edge1 = other.edge
        e_ps = map(str, edge1.edge_end_points())
        _ps = map(str, self.edge.edge_end_points())
        return set(e_ps).intersection(set(_ps))


# 点是某一条边的中点
class MiddlePointOnTheEdge(PointOnTheEdge):
    pass


# 两条直线垂直关系
class VerticalRation(LocationRelation):
    def __init__(self, first_edge, second_edge):
        self._first_edge = sympify(first_edge)
        self._second_edge = sympify(second_edge)

    @property
    def vertical_point(self):
        """
        垂点
        :return: 
        """
        edge1_points = set(map(str, Edge(self._first_edge).edge_end_points()))
        edge2_points = set(map(str, Edge(self._second_edge).edge_end_points()))
        p = str(list(edge1_points.intersection(edge2_points))[0])
        return Point(p)

    def vertical_edges(self):
        """
        相互垂直的两条边
        :return: 
        """
        return Edge(self._first_edge), Edge(self._second_edge)


# 条件等式
class ConditionEq(object):
    def to_eq(self):
        pass


# 简单等式
class SimpleEq(ConditionEq):
    def __init__(self, eq):
        self._eq = eq

    def to_eq(self):
        return self._eq


# 三角形面积等式
class TriangleAreaEq(ConditionEq):
    def __init__(self, triangle, area_value):
        if isinstance(triangle, BaseTriangle):
            self._triangle = triangle
        elif isinstance(triangle, str):
            self._triangle = BaseSanJiaoXing(*list(triangle))
        self._area_value = sympify(area_value)

    def to_eq(self):
        edges = list(map(lambda _: sympify(str(_)), self._triangle.triangle_edges))
        area_expr = (edges[0] + edges[1] + edges[2]) * (edges[0] + edges[1] - edges[2]) * (
            edges[0] + edges[2] - edges[1]) * (edges[1] + edges[2] - edges[0])
        return [area_expr, (4 * self._area_value) ** 2]


ABC_ANGLE_SUBS = (('A', 'BAC'), ('B', 'ABC'), ('C', 'ACB'))
ABC_LINES_SUBS = (('Line(A, B)', 'AB'), ('Line(A, C)', 'AC'), ('Line(B, C)', 'BC'),
                  ('c', 'AB'), ('b', 'AC'), ('a', 'BC'))


# 基础三角形对象
# 会根据条件来更新三角形
# 根据条件 推导出等式
# 根据等式列出方程组 求解
# 因为是三角形问题 所以要学会构造三角形
# sub_traingles 子三角形
# 所以来了任何关系 首先要找出 关系所属的三角形， 然后在判断 能不能根据关系 构造出新的三角形
class BaseSanJiaoXing(object):
    @staticmethod
    def _triangle_angle_sum_eq(triangle_points):
        _p1, _p2, _p3 = triangle_points
        _A1 = Angle(str(_p1) + str(_p2) + str(_p3))
        _A2 = Angle(str(_p1) + str(_p3) + str(_p2))
        _A3 = Angle(str(_p2) + str(_p1) + str(_p3))
        return [sympify(str(_A1) + '+' + str(_A2) + '+' + str(_A3)), pi]

    def __init__(self, p1, p2, p3, edge_alias=None, angle_alias=None, value=None):
        self.p1 = Point(p1)  # 点1
        self.p2 = Point(p2)  # 点2
        self.p3 = Point(p3)  # 点3
        self._triangle_points = [p1, p2, p3]  # 构成三角形的三个点
        self._triangle_edges = list(map(Edge, [str(p1) + str(p2), str(p1) + str(p3), str(p2) + str(p3)]))  # 三角形的三条边
        self._direct_sub_triangles = []  # 直属子三角形
        self._sub_points = []  # 所包含的点
        self._cond_eqs = []  # 包含的条件等式:解三角形的关键
        self._angle_eqs = []  # 只存储 角关系的等式
        self._angle_eqs.append(BaseSanJiaoXing._triangle_angle_sum_eq([p1, p2, p3]))
        # 首先如果建立一个角之后 那么 它三个内角
        self._known = {}  # 存储某些键值的，用于search 值存储那些 单变量的值
        self.value = value
        self.edge_alias = edge_alias  # 边的别名
        self.angle_alias = angle_alias  # 角的别名
        self._relations = []
        self._collineations = [[self.p1.point_name, self.p2.point_name], [self.p1.point_name, self.p3.point_name],
                               [self.p2.point_name, self.p3.point_name]]  # 共线列表

        self._solved = False
        self._edge_values = []
        self.verify_angle_eqs = []  # 用于验证的 比如给定了一个角为60度  那么因为使用了 cos(x)**2 = cos_expr**2 左边是表达式， 右边是值
        self._steps = []  # 解题步骤
        self._labels = []  # 标签

    def get_steps(self):
        return self._steps

    def add_step(self, _step):
        self._steps.append(_step)

    @property
    def labels(self):
        return self._labels

    def add_label(self, lable):
        self._labels.append(lable)

    @property
    def triangle_points(self):
        """
        三角形的三个顶点
        :return: 
        """
        return self._triangle_points

    @property
    def triangle_edges(self):
        """
        三角形的三条边
        :return: 
        """
        return self._triangle_edges

    @property
    def triangle_angles(self):
        """
        三角形的三个角
        :return: 
        """
        points = map(str, self.triangle_points)
        angles = []
        for angle_point in points:
            edge_points = sorted(list(set(points) - {angle_point}))
            angle = Angle(edge_points[0] + angle_point + edge_points[1])
            angles.append(angle)
        return angles

    def sub_points(self):
        """
        三角形所包含的点
        :return: 
        """
        return self._sub_points

    def add_sub_point(self, point):
        """
        添加一个子点
        :param point: 
        :return: 
        """
        self._sub_points.append(Point(point))

    def _add_col_line(self, p1, p2, p3):
        """
        添加点共线。直线上三点
        :param p1: 点1 
        :param p2: 点2
        :param p3: 点3
        :return: 
        """
        ps = [str(p1), str(p2), str(p3)]
        ps_combins = list(combinations(ps, 2))
        _col_line = None
        line = [str(p1), str(p2), str(p3)]
        for col_line in self._collineations:
            for _ps_comb in ps_combins:
                if set(_ps_comb).issubset(col_line):
                    _col_line = col_line
        if not _col_line:
            self._collineations.append(line)
        else:
            _col_line.extend(line)

    def points_col_lined(self, *ps):
        """
        点是否共线
        :param ps: 
        :return: 
        """
        _ps = map(str, ps)
        for col_line in self._collineations:
            if set(_ps).issubset(set(col_line)):
                return True
        return False

    @property
    def get_direct_sub_triangles(self):
        """
        获取直属子三角形
        :return: 
        """
        return self._direct_sub_triangles

    def add_direct_sub_triangle(self, triangle):
        """
        添加直属子三角形
        :param triangle: 
        :return: 
        """
        self._direct_sub_triangles.append(triangle)

    def get_sub_triangles(self):
        """
        当前三角形所包含的的子三角形形
        :return: 
        """
        sub_traingles_queue = deque()
        sub_traingles_queue.append(self._direct_sub_triangles)
        sub_traingles = []
        while sub_traingles_queue:
            tmp_sub_triangles = sub_traingles_queue.pop()
            for triangle in tmp_sub_triangles:
                sub_traingles.append(triangle)
                sub_traingles_queue.append(triangle.get_direct_sub_triangles)
        return sub_traingles

    def edge_oppo_angle(self, edge):
        """
        边所对应的角
        :param edge: 
        :return: 
        """
        vs = sorted(map(str, self.triangle_points))
        edge = Edge(edge)
        edge_end_points = sorted(map(str, edge.edge_end_points()))
        angle_point = str(list(set(vs) - set(edge_end_points))[0])
        angle = Angle(edge_end_points[0] + angle_point + edge_end_points[1])
        return angle

    def angle_oppo_edge(self, angle):
        """
        角所对应的边
        :param angle: 
        :return: 
        """
        angle = angle if isinstance(angle, Angle) else Angle(angle)
        angle_point = {str(angle.angle_point)}
        edge_points = sorted(list(set(map(str, self.triangle_points)) - angle_point))
        return Edge(''.join(edge_points))

    def get_edge_triangle(self, edge):
        """
        边所在的三角形
        :param edge: 
        :return: 
        """
        if edge in self.triangle_edges:
            return self
        for triangle in self.get_sub_triangles():
            if edge in triangle.triangle_edges:
                return edge
        return None

    def get_angle_triangle(self, angle):
        """
        角所在的三角形
        :param angle: 
        :return: 
        """
        angle = Angle(angle)
        if angle in self.triangle_angles:
            return self
        for triangle in self.get_sub_triangles():
            if angle in triangle.triangle_angles:
                return triangle
        return None

    def _update_known(self, k, v):
        """
        更新搜寻条件的
        :param k: 
        :param v: 
        :return: 
        """
        self._known[sympify(k)] = v

    def search(self, k_symbol):
        """
        搜寻单变量的值
        :param k_symbol: 
        :return: 
        """
        return self._known.get(sympify(k_symbol), None)

    @property
    def known_subs(self):
        """
        已知的可以替换的subs
        :return: 
        """
        return self._known.items()

    @property
    def all_angles(self):
        """
        三角形内所有角
        :return: 
        """
        _angles = []
        _angles.extend(self.triangle_angles)  # 先把自身的三个角包含在内
        for triangle in self.get_sub_triangles():
            _angles.extend(triangle.triangle_angles)
        return _angles

    def same_angle(self, angle):
        """
        相同的角
        :param angle: 
        :return: 
        """
        _angles = []
        _angle = Angle(angle)
        _a_p1, _a_p2 = _angle.edge_points  # 角的两个边的点
        for a in self.all_angles:
            if str(_angle.angle_point) == str(a.angle_point) and str(_angle) != str(a):  # 首先要保证角点相同，并且不能是当前角,才能继续下去
                ag_p = _angle.angle_point
                a_p1, a_p2 = a.edge_points
                if self.points_col_lined(ag_p, _a_p1, a_p1) and self.points_col_lined(ag_p, _a_p2, a_p2):
                    _angles.append(a)
                if self.points_col_lined(ag_p, _a_p1, a_p2) and self.points_col_lined(ag_p, _a_p2, a_p1):
                    _angles.append(a)
        return _angles

    def _angle_cos(self, angle):
        """
        使用余弦定理表达角的cos值
        :param angle: 
        :return: 
        """
        angle = Angle(str(angle))
        angle_triangle = self.get_angle_triangle(angle)  # 角所在的三角形
        adj_edge1, adj_edge2 = map(lambda _: sympify(str(_)), angle.angle_adj_edges)  # 角的领边
        angle_o_edge = sympify(str(angle_triangle.angle_oppo_edge(angle)))  # 角的对边
        # cos表示
        _cos = (adj_edge1 ** 2 + adj_edge2 ** 2 - angle_o_edge ** 2) / (2 * adj_edge1 * adj_edge2)
        return _cos

    def update_condition(self, cond):
        """
        更新条件 条件只能是字符串组成的条件。这里主要目的是更新搜寻条件。
        1 只要是那些可以求出单变量的值，都更新到搜寻条件里面
        2 将类似 不同变量代表相同边和角统一
        :param cond: 条件
        :return: 
        """
        if isinstance(cond, ConditionEq):
            cond = cond.to_eq()
        left, right = list(map(sympify, map(str, cond)))
        f = left - right
        f = su.radianed(f)
        f = expand_trig(f)
        f = f.subs(ABC_ANGLE_SUBS)
        f = f.subs(ABC_LINES_SUBS)
        symbs = list(f.free_symbols)
        symb = symbs[0]
        if len(symbs) == 1:
            symb_values = co.isolve_eq2(BaseEq([f, '0']))
            if len(symbs) == 1 and symb_values[0].is_real:
                self._update_known(symb, symb_values[0])
        for symb in symbs:
            symb_str = str(symb)
            if len(symb_str) == 2:
                symb_sub = ''.join(sorted(list(symb_str)))
                f = f.subs(symb, symb_sub)
            elif len(symb_str) == 3:
                symb_sub = str(Angle(symb_str))
                f = f.subs(symb, symb_sub)
        angled_eq = reduce(lambda _1, _2: _1 and _2, map(lambda _: len(str(_)) == 3, symbs))
        if angled_eq:  # 如果是角的关系
            self._angle_eqs.append([f, '0'])
        else:  # 否则就放到边的关系里面
            self._cond_eqs.append([f, '0'])

    def update_ration(self, ration):
        """
        更新关系
        :param ration: 
        :return: 
        """
        self._relations.append(ration)
        if isinstance(ration, PointOnTheEdge):  # 点在边上的关系
            point = ration.point  # 点
            edge = ration.edge  # 边
            e_p1, e_p2 = edge.edge_end_points()
            self._add_col_line(point, e_p1, e_p2)
            if str(point) not in map(str, self.sub_points()):  # 如果顶点没有出现在顶点列表中  说明这个关系之前没有
                edge_points = list(map(str, edge.edge_end_points()))  # 边的两个点
                self._add_col_line(point, *edge_points)
                triangle = self.get_edge_triangle(edge)  # 边所在的三角形
                edge_o_angle_point = str(triangle.edge_oppo_angle(edge).angle_point)  # 边所对应的角
                # 构造了两个新的三角形
                t1_points = sorted(map(str, [point, edge_o_angle_point, edge_points[0]]))
                edge1 = Edge(''.join(sorted(map(str, [point, edge_points[0]]))))
                t2_points = sorted(map(str, [point, edge_o_angle_point, edge_points[1]]))
                edge2 = Edge(''.join(sorted(map(str, [point, edge_points[1]]))))
                t1 = BaseTriangle(*t1_points)
                t2 = BaseTriangle(*t2_points)
                triangle.add_direct_sub_triangle(t1)
                triangle.add_direct_sub_triangle(t2)
                self.add_step(['由点' + str(point) + '在边' + str(edge) + '上, 可以得出',
                               BaseEq([str(edge1) + '+' + str(edge2), str(edge)]).printing()])
                self.update_condition([str(edge1) + '+' + str(edge2), str(edge)])
                angle1 = Angle(edge_o_angle_point + str(point) + str(edge_points[0]))
                angle2 = Angle(edge_o_angle_point + str(point) + str(edge_points[1]))
                self.update_condition([str(angle1) + '+' + str(angle2), pi])  # 两个角相加为pi
                angle1_cos = self._angle_cos(angle1)
                angle2_cos = self._angle_cos(angle2)
                self.update_condition([str(angle1_cos) + '+' + str(angle2_cos), '0'])  # 连个互为补角的余弦值相加为0
                if isinstance(ration, MiddlePointOnTheEdge):
                    self.add_step(['又点' + str(point) + '为' + str(edge) + '的中点,',
                                   '所以 ' + BaseEq([str(edge1), str(edge2)]).printing()])
                    self.update_condition([str(edge1), str(edge2)])
        elif isinstance(ration, VerticalRation):  # 垂直关系 例如 AD垂直于AC 则在三角形ADC中讨论
            e1, e2 = ration.vertical_edges()  # 两条边
            e1_points = e1.edge_end_points()
            e2_points = e2.edge_end_points()
            p1 = list(set(e1_points) - {ration.vertical_point})[0]
            p2 = list(set(e2_points) - {ration.vertical_point})[0]
            e3 = Edge(str(p1) + str(p2))
            _eq = ['%s**2 + %s**2' % (str(e1), str(e2)), '%s**2' % str(e3)]
            self._steps.append([r'因为 \[%s \bot %s\]' % (str(e1), str(e2)), ' 所以' + BaseEq(_eq).printing()])
            self.update_condition(SimpleEq(_eq))

    def __eq__(self, other):
        if isinstance(other, str):
            other = BaseTriangle(*list(other))
        points1 = map(str, self.triangle_points)
        points2 = map(str, other.triangle_points)
        return set(points1) == set(points2)

    def _angle_2_edge_cond(self, angle_cond):
        """
        角化边关系
        :param angle_cond: 
        :return: 
        """
        l, r = map(sympify, angle_cond)
        f = l - r
        f = f.subs(self.known_subs)
        for symb in f.free_symbols:
            if len(str(symb)) == 3:  # 角
                angle = Angle(str(symb))
                angle_triangle = self.get_angle_triangle(angle)  # 角所在的三角形
                adj_edge1, adj_edge2 = map(lambda _: sympify(str(_)), angle.angle_adj_edges)  # 角的领边
                angle_o_edge = sympify(str(angle_triangle.angle_oppo_edge(angle)))  # 角的对边
                # cos转换
                cos_subs = (cos(symb),
                            (adj_edge1 ** 2 + adj_edge2 ** 2 - angle_o_edge ** 2) / (2 * adj_edge1 * adj_edge2))
                f = f.subs(*cos_subs)
                # sin转换 是否有必要?
        return [f, '0']

    def _solve_angles(self):
        """
        解只含有角的等式
        :return: 
        """
        _angle_symbs = co.eqs_symbs(self._angle_eqs)
        _angle_symbs_map = {}
        _tmp_symbs = []
        for index, _angle_symb in enumerate(_angle_symbs):
            sub_symb = 'x' + str(index)
            _angle_symbs_map[_angle_symb] = sub_symb
            _tmp_symbs.append(sub_symb)
        _angle_symb_subs = _angle_symbs_map.items()
        _angle_symbs_reverse_map = {v: k for k, v in _angle_symbs_map.items()}
        sub_symb_eqs = map(lambda _: [sympify(_[0]).subs(_angle_symb_subs), sympify(_[1]).subs(_angle_symb_subs)],
                           self._angle_eqs)
        try:
            _angle_values = co.isolve_eqs(BaseEqs(sub_symb_eqs), _tmp_symbs)[0]
        except Exception:  # 如果求不出 则返回空
            return []
        _angle_symbs_reverse_subs = _angle_symbs_reverse_map.items()
        _angle_values = map(lambda _: [_[0].subs(_angle_symbs_reverse_subs), _[1].subs(_angle_symbs_reverse_subs)],
                            _angle_values)
        return _angle_values

    def _cond_process(self):
        """
        条件处理.全部转换为边的关系。就是可以表示为sympy的表达式
        :return: 
        """

        def _single_angle_process(tmp_f):
            """
            单变量(角)的处理
            :param tmp_f: 
            :return: 
            """
            symb = list(sympify(tmp_f).free_symbols)[0]
            _eq = [tmp_f, '0']
            if len(str(symb)) == 3:  # 如果是角
                angle = Angle(str(symb))
                angle_triangle = self.get_angle_triangle(angle)  # 角所在的三角形
                adj_edge1, adj_edge2 = map(lambda _: sympify(str(_)), angle.angle_adj_edges)  # 角的领边
                angle_o_edge = sympify(str(angle_triangle.angle_oppo_edge(angle)))  # 角的对边
                cos_expr = (adj_edge1 ** 2 + adj_edge2 ** 2 - angle_o_edge ** 2) / (2 * adj_edge1 * adj_edge2)
                if tmp_f.subs(sin(symb), 1) != tmp_f or tmp_f.subs(cos(symb), 1) != tmp_f:
                    angle_value = co.split_mons_const(su.solve_trig_eq(BaseEq([tmp_f, '0']))[0])[1]
                else:
                    angle_value = co.isolve_eq2(BaseEq([tmp_f, '0']))[0]
                _eq = [cos(angle_value) ** 2, cos_expr ** 2]  # 一定要加上平方，因为cos的正负无法确定
                self.add_label('余弦定理')
                self.add_step(['由' + BaseEq([tmp_f, '0']).printing(), '由余弦定理得出:' + BaseEq(_eq).printing()])
                self.verify_angle_eqs.append([cos_expr, cos(angle_value)])  # 用于验证
            return _eq

        new_cond_eqs = []
        for index, eq in enumerate(self._cond_eqs):
            f = sympify(eq[0]) - sympify(eq[1])
            symbs = list(f.free_symbols)
            if len(symbs) == 1 and len(str(symbs[0])) == 3:  # 如果只有一个变量并且是角变量
                new_cond_eqs.append(_single_angle_process(f))
            elif len(symbs) == 1 and len(str(symbs[0])) == 2:  # 如果只有一个变量并且是边变量
                new_cond_eqs.append([f, '0'])
            elif len(f.subs(self.known_subs).free_symbols) == 1:  # 如果被替换掉后可以变为单变量的值
                f = f.subs(self.known_subs)
                new_cond_eqs.append(_single_angle_process(f))
            # 如果是两条边，并且相等
            elif co.split_mons_const(f)[1] == 0 and reduce(lambda _1, _2: _1 and _2,
                                                           map(lambda _: len(str(_)) == 2, symbs)) and len(symbs) == 2:
                new_cond_eqs.append([f, '0'])
                line1, line2 = map(str, symbs)
                line1_coef = f.coeff(line1)
                line2_coef = f.coeff(line2)
                if abs(line1_coef / line2_coef) != 1:
                    continue
                line1_points = list(line1)
                line2_points = list(line2)
                int_ps = set(line1_points).intersection(set(line2_points))
                if int_ps and not self.points_col_lined(*set(chain(line1_points, line1_points))):  # 并且是相邻的两条边[三角不能共线的]
                    int_p = list(int_ps)[0]  # 交点
                    lp1 = list(set(line1_points) - {int_p})[0]  # 点1
                    lp2 = list(set(line2_points) - {int_p})[0]  # 点2
                    a1 = Angle(int_p + lp1 + lp2)  # 角1
                    a1_e1, a1_e2 = map(lambda _: sympify(str(_)), a1.angle_adj_edges)  # 角的领边
                    a1_t = self.get_angle_triangle(a1)
                    a1_e3 = sympify(str(a1_t.angle_oppo_edge(a1)))  # 角的对边
                    cos_a1 = sjx_utils.cos_law(a1_e1, a1_e2, a1_e3)
                    a1_same_ags = self.same_angle(a1)  # 相同的角
                    if a1_same_ags:
                        a1_s = a1_same_ags[0]
                        a1_t = self.get_angle_triangle(a1_s)
                        e1, e2 = map(lambda _: sympify(str(_)), a1_s.angle_adj_edges)  # 角的领边
                        e3 = sympify(str(a1_t.angle_oppo_edge(a1_s)))  # 角的对边
                        new_cond_eqs.append([cos_a1, sjx_utils.cos_law(e1, e2, e3)])
                    # 角a1 = 角a2
                    a2 = Angle(int_p + lp2 + lp1)  # 角2
                    a2_same_ags = self.same_angle(a2)  # 相同的角
                    a2_e1, a2_e2 = map(lambda _: sympify(str(_)), a2.angle_adj_edges)  # 角的领边
                    a2_t = self.get_angle_triangle(a2)
                    a2_e3 = sympify(str(a2_t.angle_oppo_edge(a2)))  # 角的对边
                    cos_a2 = sjx_utils.cos_law(a2_e1, a2_e2, a2_e3)
                    new_cond_eqs.append([cos_a1, cos_a2])
                    if a2_same_ags:
                        a2_s = a2_same_ags[1]
                        a2_t = self.get_angle_triangle(a2_s)
                        e1, e2 = map(lambda _: sympify(str(_)), a2_s.angle_adj_edges)  # 角的领边
                        e3 = sympify(str(a2_t.angle_oppo_edge(a2_s)))  # 角的对边
                        new_cond_eqs.append([cos_a2, sjx_utils.cos_law(e1, e2, e3)])
            else:
                eq = self._angle_2_edge_cond(eq)
                new_cond_eqs.append(eq)
        angle_values = self._solve_angles()
        if angle_values:
            for _angle, _value in angle_values:
                if _value.is_real:
                    new_cond_eqs.append(_single_angle_process(_angle - _value))
        else:  # 如果不能将角关系方程组中的式子解出来, 那么单独求解
            for _a_eq in self._angle_eqs:
                _a_eq = list(map(sympify, _a_eq))
                f = _a_eq[0] - _a_eq[1]
                symbs = list(f.free_symbols)
                if len(symbs) == 1 and len(str(symbs[0])) == 3:
                    new_cond_eqs.append(_single_angle_process(f))
        self._cond_eqs = new_cond_eqs

    def _verify_values(self, values):
        def _legal_sides(side_values):
            """
            是否是合法的三边的值. 是否满足两边之和大于第三边。
            ((a, c/2), (b /c/4), (c, c))
            :param side_values:
            :return:
            """
            pers = permutations(side_values, 3)
            for per in pers:
                f_v, s_v, t_v = per
                if not solveset(str(f_v + s_v - t_v) + '>0', domain=S.Reals).intersect(
                        Interval(0, S.Infinity, left_open=True)):
                    return False
            return True

        postived = reduce(lambda _1, _2: _1 and _2, map(lambda _: _[1] > 0, values))
        if not postived:  # 首先满足所有值为正数
            return False

        all_triangles = [self, ]  # 所有的是三角形
        all_triangles.extend(self.get_sub_triangles())
        side_values_map = dict(values)
        for triangle in all_triangles:
            edges = map(lambda _: sympify(str(_)), triangle.triangle_edges)
            edge_values = map(lambda _: side_values_map[_], edges)
            if not _legal_sides(edge_values):
                return False
        return True

    def solve_triangle(self):
        """
        解三角形 求出三角形中每个边
        :return: 
        """
        self._cond_process()  # 预处理
        symbs = co.eqs_symbs(self._cond_eqs)
        symbs_map = {}
        new_symbs = []
        for index, symb in enumerate(symbs):
            symbs_map[symb] = 'x' + str(index)
            new_symbs.append('x' + str(index))
        symbs_subs = symbs_map.items()
        reverse_symbs_map = {v: k for k, v in symbs_map.items()}
        reverse_symbs_subs = reverse_symbs_map.items()
        self.add_step(['得到方程组:', BaseEqs(self._cond_eqs).printing()])
        cond_eqs = BaseEqs(list(map(lambda _: [sympify(_[0]).subs(symbs_subs), sympify(_[1]).subs(symbs_subs)], self._cond_eqs)))  # 得出的方程组
        symb_values_list = co.isolve_eqs(cond_eqs, new_symbs)
        symb_values_list = list(map(lambda _: list(map(lambda pair: (pair[0].subs(reverse_symbs_subs), pair[1]), _)), symb_values_list))
        # 当含有多个结果的时候,那么必然是因为使用 [cos(angle_value) ** 2, cos_expr ** 2]
        symb_values_list = list(filter(lambda _: self._verify_values(_), symb_values_list))
        self._solved = True
        if len(symb_values_list) == 1:
            self._edge_values = symb_values_list[0]
        else:  # 此时就要验证了
            new_symb_values_list = []
            for svs in symb_values_list:
                svs = list(svs)
                _flag = True
                svs = map(lambda _: (_[0], simplify(_[1])), svs)
                svs = list(svs)
                _verify_eqs = filter(lambda _: _[0].subs(svs) * _[1] > 0 and simplify(_[0].subs(svs) ** 2) == simplify(_[1] ** 2), self.verify_angle_eqs)
                _verify_eqs = list(_verify_eqs)
                if len(_verify_eqs) != len(self.verify_angle_eqs):
                    _flag = False
                if _flag:
                    new_symb_values_list.append(svs)
            self._edge_values = new_symb_values_list[0]
        self.add_step(['验证解得各边长:', BaseEqs(self._edge_values).printing()])
        return self._edge_values

    def solve_all_values(self):
        """
        解出三角形中所有可以求出的值 角的值和边的值
        :return: 
        """
        if not self._solved:
            self.solve_triangle()
        values = [edge_value for edge_value in self._edge_values]
        angles = self.all_angles  # 所有角
        for angle in angles:
            adj1, adj2 = map(lambda _: sympify(str(_)), angle.angle_adj_edges)  # 角的两临边
            op_edge = sympify(str(self.angle_oppo_edge(angle)))  # 角的对边
            cos_value = sjx_utils.cos_law(adj1, adj2, op_edge).subs(self._edge_values)
            sin_value = sqrt(1 - cos_value ** 2)
            values.append((cos(str(angle)), cos_value))
            values.append((sin(str(angle)), sin_value))
        return values


if __name__ == '__main__':
    pass
